package com.javacoo.xservice.base.support.handler.internal;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

import com.javacoo.xservice.base.BaseRequest;
import com.javacoo.xservice.base.ErrorCodeConstants;
import com.javacoo.xservice.base.bean.LockBean;
import com.javacoo.xservice.base.config.Resources;
import com.javacoo.xservice.base.exception.IllegalParameterException;
import com.javacoo.xservice.base.support.lock.Lock;
import com.javacoo.xservice.base.support.lock.config.LockConfig;
import com.javacoo.xservice.base.support.swaparea.SwapAreaUtils;
import com.javacoo.xservice.base.utils.DataUtil;
import com.javacoo.xservice.base.utils.ExpressionUtil;
import com.javacoo.xservice.base.utils.LogUtil;

/**
 * 方法加锁处理
 * <li></li>
 * @author duanyong@jccfc.com
 * @date 2021/3/4 15:38
 */
@Component
@ConditionalOnBean(LockConfig.class)
public class MethodLockHandler extends AbstractHandler{
    /**锁对象*/
    @Autowired
    private Lock lock;
    /**方法加锁配置*/
    @Autowired
    private LockConfig lockConfig;

	@Override
	public void preHandle(Object input) {
        if(!(input instanceof BaseRequest)){
            return;
        }
        BaseRequest req = (BaseRequest)input;

        String methodName = SwapAreaUtils.getSwapAreaData().getReqMethod();
        if(lockConfig.getLockParamMap() == null || lockConfig.getLockParamMap().get(methodName) == null){
            return;
        }
        try{
            String cacheKey = getCacheKey(methodName,req.getParameter());
            LockBean lockBean = lockConfig.getLockParamMap().get(methodName);
            Integer timeout = lockBean.getSecondTimeout() == null ? lockConfig.getSecondTimeout() : lockBean.getSecondTimeout();
            LogUtil.info("方法进入分布式事务锁,加锁key:{},自动失效时间:{}秒",cacheKey,timeout);
            boolean isLocked = tryLock(cacheKey,timeout);
            if(!isLocked){
                throw new IllegalParameterException(Resources.getMessage(ErrorCodeConstants.COMMON_REQ_LOCK_FAILURE));
            }
		}catch(Exception ex){
			ex.printStackTrace();
            LogUtil.error("加入分布式锁发生异常,MethodName:{},Exception:{}",methodName,ex);
		}
	}
    /**
     * 获取缓存key
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/3/4 16:09
     * @param methodName:方法名称
     * @param param:业务对象
     * @return: java.lang.String 缓存key
     */
    private String getCacheKey(String methodName, Optional<Object> param) {
        LockBean lockBean = lockConfig.getLockParamMap().get(methodName);
        if(StringUtils.isBlank(lockBean.getParams()) || !param.isPresent()){
            return methodName;
        }
        Map<String,Object> returnMap = DataUtil.toMap(param.get());
        String[] elArr =  lockBean.getParams().split(",");
        List<String> fieldValueList = new ArrayList<>(5);
        for(int i=0;i<elArr.length;i++){
            ExpressionUtil.expressionParser(elArr[i],returnMap).ifPresent(s->{
                fieldValueList.add(s);
            });
        }
        if(fieldValueList.isEmpty()){
            return methodName;
        }
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(methodName)
            .append("-")
            .append(fieldValueList.stream().collect(Collectors.joining("-")));
        return keyBuilder.toString();
    }
    /**
     * 获取分布式锁
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/3/4 16:53
     * @param cacheKey: 缓存key
     * @param timeout: 超时时间
     * @return: boolean
     */
    private boolean tryLock(String cacheKey,int timeout){
        timeout = timeout <= 0 || timeout > 5*60 ? Lock.TIMEOUT_SECOND : timeout;
        boolean isLocked = lock.tryLock(cacheKey, TimeUnit.SECONDS,0,timeout);
        if(isLocked){
            SwapAreaUtils.getSwapAreaData().setCacheKey(cacheKey);
            LogUtil.info("加锁成功,KEY:{},自动失效时间:{}秒",cacheKey,timeout);
        }
        return isLocked;
    }
    /**
     * 索引
     * <li>自然排序</li>
     *
     * @author duanyong@jccfc.com
     * @date 2021/3/4 11:06
     * @return: int
     */
    @Override
    public int index() {
        return 0;
    }
}
